#include <stdio.h>
#include <Windows.h>
#include <Ntddscsi.h>
#include <winioctl.h>
#include <tchar.h>
#include "../scsi.h"
#include "xusbdevice.h"
#include "xusb_common.h"

#define SPT_SENSE_LENGTH 32

typedef struct _SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER
{
    SCSI_PASS_THROUGH_DIRECT sptd;
    int             Filler;      // realign buffer to double word boundary
    UCHAR             ucSenseBuf[SPT_SENSE_LENGTH];
} SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER, *PSCSI_PASS_THROUGH_DIRECT_WITH_BUFFER;


void *Scsi_Open(PXUSBDevice pDevice)
{
    char szPath[MAX_PATH] = {0x0};
    if(0 != pDevice->chDrive)
    {
        sprintf(szPath, "\\\\?\\%c:", pDevice->chDrive);
    }
    else
    {
        sprintf(szPath, "%s", pDevice->szDevicePath);
    }
    void *hDevice = CreateFileA(szPath,
                                GENERIC_READ | GENERIC_WRITE,
                                FILE_SHARE_READ | FILE_SHARE_WRITE,
                                NULL,
                                OPEN_EXISTING,
                                0,
                                NULL);
    if(0 != pDevice->chDrive && INVALID_HANDLE_VALUE == hDevice)
    {
        sprintf(szPath, "%s", pDevice->szDevicePath);
        hDevice = CreateFileA(szPath,
                              GENERIC_READ | GENERIC_WRITE,
                              FILE_SHARE_READ | FILE_SHARE_WRITE,
                              NULL,
                              OPEN_EXISTING,
                              0,
                              NULL);
    }
    if(INVALID_HANDLE_VALUE == hDevice)
    {
        hDevice = NULL;
    }
    return hDevice;
}

BOOL Scsi_Transmit(void *hDevice, UCHAR *Cdb, int cdbSize, UCHAR DataIn, unsigned char *DataBuffer, int DataTransferLength, int TimeOutValue)
{
    BOOL status = FALSE;

    if(hDevice != INVALID_HANDLE_VALUE)
    {
        DWORD length = 0;
        DWORD returned = 0;
        SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER sptdwb;

        ZeroMemory(&sptdwb, sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER));
        CopyMemory(&sptdwb.sptd.Cdb[0], Cdb, cdbSize);

        sptdwb.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT);
        sptdwb.sptd.PathId = 0;
        sptdwb.sptd.TargetId = 1;
        sptdwb.sptd.Lun = 0;
        sptdwb.sptd.CdbLength = cdbSize;
        sptdwb.sptd.DataIn = DataIn;
        sptdwb.sptd.SenseInfoLength = SPT_SENSE_LENGTH;
        sptdwb.sptd.DataTransferLength = DataTransferLength;
        sptdwb.sptd.TimeOutValue = TimeOutValue;
        sptdwb.sptd.DataBuffer = DataBuffer;
        sptdwb.sptd.SenseInfoOffset = sizeof(SCSI_PASS_THROUGH_DIRECT) + 4;

        length = sizeof(SCSI_PASS_THROUGH_DIRECT_WITH_BUFFER);
        status = DeviceIoControl(hDevice,
                                 IOCTL_SCSI_PASS_THROUGH_DIRECT,
                                 &sptdwb,
                                 length,
                                 &sptdwb,
                                 length,
                                 &returned,
                                 NULL);
        DWORD error = 0;
        error = GetLastError();
    }

    return status;
}

BOOL Scsi_Write(void *hDevice, unsigned char *DataBuffer, int DataTransferLength, int TimeOutValue)
{
    unsigned char cdb[10] = {0};
    int ret = 0;

    cdb[0] = 0xEF;
    cdb[1] = 0xFE;

    ret = Scsi_Transmit(hDevice, cdb, 10, 0, DataBuffer, DataTransferLength, TimeOutValue);

    return ret;
}

BOOL Scsi_Read(void *hDevice, unsigned char *DataBuffer, int DataTransferLength, int TimeOutValue)
{
    unsigned char cdb[10] = {0};
    int ret = 0;

    cdb[0] = 0xEF;
    cdb[1] = 0xFF;

    ret = Scsi_Transmit(hDevice, cdb, 10, 1, DataBuffer, DataTransferLength, TimeOutValue);

    return ret;
}

void Scsi_Close(void *hDevice)
{
    if((hDevice != INVALID_HANDLE_VALUE))
    {
        CloseHandle(hDevice);
        hDevice = INVALID_HANDLE_VALUE;
    }
}

#define INQUIRY_LENGTH                0x24
int Scsi_GetType(void *hDevice)
{

    unsigned char cdb[16] = {0};
    unsigned char buffer[64] = {0x0};
    int ret = 0;
    memset(buffer, 0x0, 64);
    memset(cdb, 0x0, 16);
    cdb[0] = 0x12;
    cdb[4] = INQUIRY_LENGTH;

    ret = Scsi_Transmit(hDevice, cdb, 16, 1, buffer, INQUIRY_LENGTH, 1 * 1000);
    if(buffer[0] == 0x05)
    {
        return 1;
    }
    return ret;
}

int Scsi_Inquiry(void *hDevice, unsigned char *buffer)
{
    unsigned char cdb[16] = {0};
    int ret = 0;
    memset(buffer, 0x0, 64);
    memset(cdb, 0x0, 16);
    cdb[0] = 0x12;
    cdb[4] = INQUIRY_LENGTH;

    ret = Scsi_Transmit(hDevice, cdb, 16, 1, buffer, INQUIRY_LENGTH, 1 * 1000);
    return ret;
}
